/*
 * libSincroInodos.c
 *
 *  Created on: 05/07/2012
 *      Author: utnso
 */

#include "libSincroInodos.h"

static nodoSincroInodo_t* pilaSincroInodo = NULL;
static pthread_mutex_t mutexUsandoListaSincro;
//static uint8_t cantInodos = 0;

void apilarNodoSincroInodo(uint32_t numInodo) {
	//cantInodos++;
	nodoSincroInodo_t* aux = (nodoSincroInodo_t*) malloc(sizeof(nodoSincroInodo_t));
	infoSincroInodo_t info;
	info.numInodo = numInodo;
	info.mutexUsandoInodo = (pthread_rwlock_t*) malloc(sizeof(pthread_rwlock_t));
	pthread_rwlock_init(info.mutexUsandoInodo, NULL);
	aux->info = info;
	aux->sig = pilaSincroInodo;
	pilaSincroInodo = aux;
}

infoSincroInodo_t desapilarNodoSincroInodo(void) {
	//cantInodos--;
	nodoSincroInodo_t* aux = pilaSincroInodo;
	infoSincroInodo_t info = pilaSincroInodo->info;
	pilaSincroInodo = pilaSincroInodo->sig;
	free(aux);
	return info;
}

uint32_t cantNodosSincroInodos(void) {
	pthread_mutex_lock(&mutexUsandoListaSincro);
	nodoSincroInodo_t* aux = pilaSincroInodo;
	uint32_t cantNodos = 0;
	while (aux != NULL) {
		cantNodos++;
		aux = aux->sig;
	}
	pthread_mutex_unlock(&mutexUsandoListaSincro);
	return cantNodos;
}

void borrarInodoNoBloqueado(uint32_t numInodo) {
	nodoSincroInodo_t* ant = pilaSincroInodo;
	nodoSincroInodo_t* aux = pilaSincroInodo;
	if ((pilaSincroInodo->info).numInodo == numInodo) {
		if (pthread_rwlock_trywrlock((aux->info).mutexUsandoInodo) == 0) {
			printf("El nodo puede ser eliminado.\n");
			//fue bloqueado por el trylock, lo desbloqueo y lo elimino
			pthread_rwlock_unlock((aux->info).mutexUsandoInodo);
			pilaSincroInodo = pilaSincroInodo->sig;
			pthread_rwlock_destroy((aux->info).mutexUsandoInodo);
			free((aux->info).mutexUsandoInodo);
			free(aux);
		} else {
			printf("El nodo esta siendo utilizado, no puede ser eliminado.\n");
			//esta bloqueado, no lo puedo eliminar
		}
	} else {
		aux = aux->sig;
		uint8_t encontrado = 0;
		while (aux != NULL && !encontrado) {
			if ((aux->info).numInodo == numInodo) {
				encontrado = 1;
				if (pthread_rwlock_trywrlock((aux->info).mutexUsandoInodo) == 0) {
					//printfCUCA("El nodo puede ser eliminado.\n");
					//fue bloqueado por el trylock, lo desbloqueo y lo elimino
					pthread_rwlock_unlock((aux->info).mutexUsandoInodo);
					ant->sig = aux->sig;
					pthread_rwlock_destroy((aux->info).mutexUsandoInodo);
					free((aux->info).mutexUsandoInodo);
					free(aux);
				} else {
					//printfCUCA("El nodo esta siendo utilizado, no puede ser eliminado.\n");
					//esta bloqueado, no lo puedo eliminar
				}
			} else {
				aux = aux->sig;
				ant = ant->sig;
			}
		}
	}
}

void vaciarListaSincroInodo(void) {
	pthread_mutex_lock(&mutexUsandoListaSincro);
	while (pilaSincroInodo != NULL) {
		infoSincroInodo_t info = desapilarNodoSincroInodo();
		pthread_rwlock_destroy(info.mutexUsandoInodo);
		free(info.mutexUsandoInodo);
	}
	pthread_mutex_unlock(&mutexUsandoListaSincro);
}

uint8_t obtenerMutexDeInodo(uint32_t numInodo, pthread_rwlock_t** mutex) {
	nodoSincroInodo_t* aux = pilaSincroInodo;
	int8_t encontrado = 0;
	while (aux != NULL && !encontrado) {
		if ((aux->info).numInodo == numInodo) {
			encontrado = 1;
		} else {
			aux = aux->sig;
		}
	}
	if (encontrado) {
		*mutex = (aux->info).mutexUsandoInodo;
		return 0;
	} else {
		return 1;
	}
}

void empiezoATrabajarEnInodo(uint32_t numInodo, uint8_t esLectura) {
	//printfCUCA("----------------------------------------------------------------------\n");
	//printfCUCA("Entro a \"empiezoATrabajarEnInodo\".\n");
	pthread_mutex_lock(&mutexUsandoListaSincro);
	//printfCUCA("Bloqueo la lista.\n");
	//printfCUCA("Busco el inodo[%d].\n", numInodo);
	pthread_rwlock_t* mutex;
	//printfCUCA("Obtengo el mutex correspondiente al inodo %d.\n", numInodo);
	int8_t error = obtenerMutexDeInodo(numInodo, &mutex);
	if (error) {
		//printfCUCA("El inodo no estaba en la lista, lo agrego.\n");
		apilarNodoSincroInodo(numInodo);
		obtenerMutexDeInodo(numInodo, &mutex);
	}
	pthread_mutex_unlock(&mutexUsandoListaSincro);
	//printfCUCA("Desbloqueo la lista.\n");
	if(esLectura){
		pthread_rwlock_wrlock(mutex);
		//printfCUCA("Bloqueo el inodo por escritura.\n");
	}else{
		pthread_rwlock_rdlock(mutex);
		//printfCUCA("Bloqueo el inodo por lectura.\n");
	}
	//printfCUCA("----------------------------------------------------------------------\n");
}

void dejoDeTrabajarEnInodo(uint32_t numInodo) {
	//printfCUCA("----------------------------------------------------------------------\n");
	//printfCUCA("Entro a \"dejoDeTrabajarEnInodo\".\n");
	pthread_mutex_lock(&mutexUsandoListaSincro);
	//printfCUCA("Bloqueo la lista.\n");
	//printfCUCA("Busco el inodo [%d].\n", numInodo);
	pthread_rwlock_t* mutex;
	//printfCUCA("Obtengo el mutex correspondiente al inodo %d.\n", numInodo);
	uint8_t error = obtenerMutexDeInodo(numInodo, &mutex);
	if (!error) {
		//printfCUCA("Encontre el mutex.\n");
		pthread_rwlock_unlock(mutex);
		//printfCUCA("Desbloqueo el inodo.\n");
	}
	//printfCUCA("Procedo a borrar el nodo correspondiente al inodo.\n");
	borrarInodoNoBloqueado(numInodo);
	pthread_mutex_unlock(&mutexUsandoListaSincro);
	//printfCUCA("Desbloqueo la lista.\n");
	//printfCUCA("----------------------------------------------------------------------\n");
}
